//
// Created by 33145 on 2023/2/1.
//
#include <memory>
#include <netinet/tcp.h>

#include "address.h"
#include "noncopyable.h"
#include "fd_manager.h"
#include "macro.h"

#ifndef SYLAR_SOCKET_H
#define SYLAR_SOCKET_H

namespace sylar {

class Socket : public std::enable_shared_from_this<Socket>, Noncopyable {
public:
  typedef std::shared_ptr<Socket> ptr;
  typedef std::weak_ptr<Socket> weak_ptr;

  enum Type {
    TCP = SOCK_STREAM,
    UDP = SOCK_DGRAM
  };
  enum Family {
    IPv4 = AF_INET,
    IPv6 = AF_INET6,
    UNIX = AF_UNIX
  };

  static Socket::ptr CreateTCP(sylar::Address::ptr address);
  static Socket::ptr CreateUDP(sylar::Address::ptr address);

  static Socket::ptr CreateTCPSocket();
  static Socket::ptr CreateUDPSocket();

  static Socket::ptr CreateTCPSocket6();
  static Socket::ptr CreateUDPSocket6();

  static Socket::ptr CreateUnixTCPSocket();
  static Socket::ptr CreateUnixUDPSocket();

  Socket(int family, int type, int protocol = 0);
  ~Socket();

  int64_t getSendTimeout();
  void setSendTimeout(int64_t v);
  int64_t getRecvTimeout();
  void setRecvTimeout(int64_t v);

  bool getOption(int level, int option, void* result, size_t* len);

  template<class T>
  bool getOption(int level, int option, T& result) {
    size_t length = sizeof(T);
    return getOption(level, option, &result, &length);
  }
  bool setOption(int level, int option, const void* result, size_t* len);

  template<class T>
  bool setOption(int level, int option, T& value) {
    size_t length = sizeof(T);
    return setOption(level, option, &value, &length);
  }

  virtual std::ostream& dump(std::ostream& os) const;

  virtual std::string toString() const;

  virtual Socket::ptr accept();

  virtual bool init(int sock);

  virtual bool bind(const Address::ptr addr);
  virtual bool connect(const Address::ptr addr, uint64_t timeout_ms = -1);
  virtual bool listen(int backlog = SOMAXCONN);
  virtual bool close();

  /// iovec 一个元素数组，可以将多组数据一次发送
  virtual int send(const void* buffer, size_t length, int flags = 0);
  virtual int send(const iovec* buffer, size_t length, int flags = 0);
  /// sendto 和 recvfrom 被用于UDP连接中， UDP不需要连接，所以要提供信息的收发端的地址
  virtual int sendTo(const void* buffer, size_t length, const Address::ptr to, int flags);
  virtual int sendTo(const iovec* buffer, size_t length, const Address::ptr to, int flags);

  virtual int recv(void* buffer, size_t length, int flags = 0);
  virtual int recvIovec(iovec* buffer, size_t length, int flags = 0) const;
  virtual int recvFrom(void* buffer, size_t length, Address::ptr from, int flags = 0);
  virtual int recvFrom(iovec* buffer, size_t length, Address::ptr from, int flags = 0) const;

  Address::ptr getRemoteAddress();
  Address::ptr getLocalAddress();

  int getFamily() const { return m_family; }
  int getType() const { return m_type; }
  int getProtocol() const { return m_protocol; }

  bool isConnected() const { return m_isConnected; }
  bool isVailed() const;
  int getError();

  int getSocket() const { return m_sock; }

  bool cancelRead();
  bool cancelWrite();
  bool cancelAccept();
  bool cancelAll();
private:
  void initSock();
  void newSock();
private:
  int m_sock;          /// 文件描述符
  int m_family;        /// 地址类型(AF_INET, AF_INET6)
  int m_type;          /// 套接字类型(SOCK_STREAM, SOCK_DGRAM)
  int m_protocol;      /// 协议类型
  bool m_isConnected;  /// 是否连接(UDP则默认已连接)

  Address::ptr m_localAddress;
  Address::ptr m_remoteAddress;
};

  std::ostream& operator<<(std::ostream& os, const Socket& sock);

}

#endif //SYLAR_SOCKET_H
